home *** CD-ROM | disk | FTP | other *** search
/ Visual Basic Graphics Programming (2nd Edition) / Visual Basic Graphics Programming 2nd Edition.iso / Src / Ch17 / Pixels.bas < prev    next >
BASIC Source File  |  1999-07-01  |  15KB  |  424 lines

  1. Attribute VB_Name = "DDBHelper"
  2. Option Explicit
  3.  
  4. ' ------------------------
  5. ' Bitmap Array Information
  6. ' ------------------------
  7. Public Type RGBTriplet
  8.     rgbBlue As Byte
  9.     rgbGreen As Byte
  10.     rgbRed As Byte
  11. End Type
  12.  
  13. ' ------------------
  14. ' Bitmap Information
  15. ' ------------------
  16. Public Type BITMAP
  17.     bmType As Long
  18.     bmWidth As Long
  19.     bmHeight As Long
  20.     bmWidthBytes As Long
  21.     bmPlanes As Integer
  22.     bmBitsPixel As Integer
  23.     bmBits As Long
  24. End Type
  25. Public Declare Function GetBitmapBits Lib "gdi32" (ByVal hBitmap As Long, ByVal dwCount As Long, lpBits As Any) As Long
  26. Public Declare Function SetBitmapBits Lib "gdi32" (ByVal hBitmap As Long, ByVal dwCount As Long, lpBits As Any) As Long
  27. Public Declare Function GetObject Lib "gdi32" Alias "GetObjectA" (ByVal hObject As Long, ByVal nCount As Long, lpObject As Any) As Long
  28.  
  29. Public Enum bmphErrors
  30.     bmphInvalidBitmapBits = vbObjectError + 1001
  31.     bmphPaletteError
  32. End Enum
  33.  
  34. ' -------------------
  35. ' Palette Information
  36. ' -------------------
  37. Private Type PALETTEENTRY
  38.     peRed As Byte
  39.     peGreen As Byte
  40.     peBlue As Byte
  41.     peFlags As Byte
  42. End Type
  43. Private Declare Function GetNearestPaletteIndex Lib "gdi32" (ByVal hPalette As Long, ByVal crColor As Long) As Long
  44. Private Declare Function GetPaletteEntries Lib "gdi32" (ByVal hPalette As Long, ByVal wStartIndex As Long, ByVal wNumEntries As Long, lpPaletteEntries As PALETTEENTRY) As Long
  45. Private Declare Function RealizePalette Lib "gdi32" (ByVal hdc As Long) As Long
  46. Private Declare Function GetSystemPaletteEntries Lib "gdi32" (ByVal hdc As Long, ByVal wStartIndex As Long, ByVal wNumEntries As Long, lpPaletteEntries As PALETTEENTRY) As Long
  47. Private Declare Function ResizePalette Lib "gdi32" (ByVal hPalette As Long, ByVal nNumEntries As Long) As Long
  48. Private Declare Function SetPaletteEntries Lib "gdi32" (ByVal hPalette As Long, ByVal wStartIndex As Long, ByVal wNumEntries As Long, lpPaletteEntries As PALETTEENTRY) As Long
  49. Private Const MAX_PALETTE_SIZE = 256
  50. Private Const PC_NOCOLLAPSE = &H4    ' Do not match color existing entries.
  51.  
  52. ' -------------------------------
  53. ' System Capabilities Information
  54. ' -------------------------------
  55. Private Declare Function GetDeviceCaps Lib "gdi32" (ByVal hdc As Long, ByVal nIndex As Long) As Long
  56. Private Const NUMRESERVED = 106  ' Number of reserved entries in system palette.
  57. Private Const SIZEPALETTE = 104  ' Size of system palette.
  58.  
  59. ' Copy memory quickly. Used for 24-bit images.
  60. Private Declare Sub CopyMemory Lib "kernel32" Alias "RtlMoveMemory" (Destination As Any, Source As Any, ByVal length As Long)
  61.  
  62. ' Load the control's palette so it matches the
  63. ' system palette.
  64. Private Sub MatchColorPalette(ByVal pic As PictureBox)
  65. Dim log_hpal As Long
  66. Dim sys_pal(0 To MAX_PALETTE_SIZE - 1) As PALETTEENTRY
  67. Dim orig_pal(0 To MAX_PALETTE_SIZE - 1) As PALETTEENTRY
  68. Dim i As Integer
  69. Dim sys_pal_size As Long
  70. Dim num_static_colors As Long
  71. Dim static_color_1 As Long
  72. Dim static_color_2 As Long
  73.  
  74.     ' Make sure pic has the foreground palette.
  75.     pic.ZOrder
  76.     RealizePalette pic.hdc
  77.     DoEvents
  78.  
  79.     ' Get system palette size and # static colors.
  80.     sys_pal_size = GetDeviceCaps(pic.hdc, SIZEPALETTE)
  81.     num_static_colors = GetDeviceCaps(pic.hdc, NUMRESERVED)
  82.     static_color_1 = num_static_colors \ 2 - 1
  83.     static_color_2 = sys_pal_size - num_static_colors \ 2
  84.  
  85.     ' Get the system palette entries.
  86.     GetSystemPaletteEntries pic.hdc, 0, _
  87.         sys_pal_size, sys_pal(0)
  88.  
  89.     ' Make the logical palette as big as possible.
  90.     log_hpal = pic.Picture.hpal
  91.     If ResizePalette(log_hpal, sys_pal_size) = 0 Then
  92.         Err.Raise bmphPaletteError, _
  93.             "DDBHelper.MatchColorPalette", _
  94.             "Error matching bitmap palette"
  95.     End If
  96.  
  97.     ' Blank the non-static colors.
  98.     For i = 0 To static_color_1
  99.         orig_pal(i) = sys_pal(i)
  100.     Next i
  101.     For i = static_color_1 + 1 To static_color_2 - 1
  102.         With orig_pal(i)
  103.             .peRed = 0
  104.             .peGreen = 0
  105.             .peBlue = 0
  106.             .peFlags = PC_NOCOLLAPSE
  107.         End With
  108.     Next i
  109.     For i = static_color_2 To 255
  110.         orig_pal(i) = sys_pal(i)
  111.     Next i
  112.     SetPaletteEntries log_hpal, 0, sys_pal_size, orig_pal(0)
  113.  
  114.     ' Insert the non-static colors.
  115.     For i = static_color_1 + 1 To static_color_2 - 1
  116.         orig_pal(i) = sys_pal(i)
  117.         orig_pal(i).peFlags = PC_NOCOLLAPSE
  118.     Next i
  119.     SetPaletteEntries log_hpal, static_color_1 + 1, static_color_2 - static_color_1 - 1, orig_pal(static_color_1 + 1)
  120.  
  121.     ' Realize the new palette.
  122.     RealizePalette pic.hdc
  123. End Sub
  124. ' Return a binary representation of the byte.
  125. ' This helper function is useful for understanding
  126. ' byte values.
  127. Public Function BinaryByte(ByVal value As Byte) As String
  128. Dim i As Integer
  129. Dim txt As String
  130.  
  131.     For i = 1 To 8
  132.         If value And 1 Then
  133.             txt = "1" & txt
  134.         Else
  135.             txt = "0" & txt
  136.         End If
  137.         value = value \ 2
  138.     Next i
  139.  
  140.     BinaryByte = txt
  141. End Function
  142.  
  143. ' Load the bits from this PictureBox into a
  144. ' two-dimensional array of RGB values. Set
  145. ' bits_per_pixel to be the number of bits per pixel.
  146. Public Sub GetBitmapPixels(ByVal pic As PictureBox, ByRef pixels() As RGBTriplet, ByRef bits_per_pixel As Integer)
  147. ' Uncomment the following to make the routine
  148. ' display information about the bitmap.
  149. ' #Const DEBUG_PRINT_BITMAP = True
  150.  
  151. Dim hbm As Long
  152. Dim bm As BITMAP
  153. Dim l As Single
  154. Dim t As Single
  155. Dim old_color As Long
  156. Dim bytes() As Byte
  157. Dim num_pal_entries As Long
  158. Dim pal_entries(0 To MAX_PALETTE_SIZE - 1) As PALETTEENTRY
  159. Dim pal_index As Integer
  160. Dim wid As Integer
  161. Dim hgt As Integer
  162. Dim X As Integer
  163. Dim Y As Integer
  164. Dim two_bytes As Long
  165.  
  166.     ' Get the bitmap information.
  167.     hbm = pic.Image
  168.     GetObject hbm, Len(bm), bm
  169.     bits_per_pixel = bm.bmBitsPixel
  170.  
  171.     ' If bits_per_pixel is 16, see if it's really
  172.     ' 15 or 16 bits per pixel.
  173.     If bits_per_pixel = 16 Then
  174.         ' Make the upper left pixel white.
  175.         l = pic.ScaleLeft
  176.         t = pic.ScaleTop
  177.         old_color = pic.Point(l, t)
  178.         pic.PSet (l, t), vbWhite
  179.  
  180.         ' See what color was set.
  181.         ReDim bytes(0 To 0, 0 To 0)
  182.         GetBitmapBits hbm, 2, bytes(0, 0)
  183.         If (bytes(0, 0) And &H80) = 0 Then
  184.             ' It's really a 15-bit image.
  185.             bits_per_pixel = 15
  186.         End If
  187.  
  188.         ' Restore the pixel's original color.
  189.         pic.PSet (l, t), old_color
  190.     End If
  191.  
  192.     #If DEBUG_PRINT_BITMAP Then
  193.         Debug.Print "*** BITMAP Data ***"
  194.         Debug.Print "bmType       "; bm.bmType
  195.         Debug.Print "bmWidth      "; bm.bmWidth
  196.         Debug.Print "bmHeight     "; bm.bmHeight
  197.         Debug.Print "bmWidthBytes "; bm.bmWidthBytes
  198.         Debug.Print "bmPlanes     "; bm.bmPlanes
  199.         Debug.Print "bmBitsPixel  "; bm.bmBitsPixel
  200.         Debug.Print "BitsPerPixel "; bits_per_pixel
  201.     #End If
  202.  
  203.     ' Get the bits.
  204.     If (bits_per_pixel = 8) Or _
  205.        (bits_per_pixel = 15) Or _
  206.        (bits_per_pixel = 16) Or _
  207.        (bits_per_pixel = 24) Or _
  208.        (bits_per_pixel = 32) _
  209.     Then
  210.         ' Get the bits.
  211.         ReDim bytes(0 To bm.bmWidthBytes - 1, 0 To bm.bmHeight - 1)
  212.         GetBitmapBits hbm, bm.bmWidthBytes * bm.bmHeight, bytes(0, 0)
  213.     Else
  214.         ' We don't know how to read this format.
  215.         Err.Raise bmphInvalidBitmapBits, _
  216.             "DDBHelper.GetBitmapPixels", _
  217.             "Invalid number of bits per pixel: " _
  218.             & Format$(bits_per_pixel)
  219.     End If
  220.  
  221.     ' Create the pixels array.
  222.     wid = bm.bmWidth
  223.     hgt = bm.bmHeight
  224.     ReDim pixels(0 To wid - 1, 0 To hgt - 1)
  225.     Select Case bits_per_pixel
  226.         Case 8
  227.             ' Match pic's palette to the system palette.
  228.             MatchColorPalette pic
  229.  
  230.             ' Get the image's palette entries.
  231.             num_pal_entries = GetPaletteEntries( _
  232.                 pic.Picture.hpal, 0, _
  233.                 MAX_PALETTE_SIZE, pal_entries(0))
  234.  
  235.             ' Get the RGB color components.
  236.             For Y = 0 To hgt - 1
  237.                 For X = 0 To wid - 1
  238.                     With pixels(X, Y)
  239.                         pal_index = bytes(X, Y)
  240.                         .rgbRed = pal_entries(pal_index).peRed
  241.                         .rgbGreen = pal_entries(pal_index).peGreen
  242.                         .rgbBlue = pal_entries(pal_index).peBlue
  243.                     End With
  244.                 Next X
  245.             Next Y
  246.  
  247.         Case 15
  248.             For Y = 0 To hgt - 1
  249.                 For X = 0 To wid - 1
  250.                     With pixels(X, Y)
  251.                         ' Get the combined 2 bytes for this pixel.
  252.                         two_bytes = bytes(X * 2, Y) + bytes(X * 2 + 1, Y) * 256&
  253.  
  254.                         ' Separate the pixel's components.
  255.                         .rgbBlue = two_bytes Mod 32
  256.                         two_bytes = two_bytes \ 32
  257.                         .rgbGreen = two_bytes Mod 32
  258.                         two_bytes = two_bytes \ 32
  259.                         .rgbRed = two_bytes
  260.                     End With
  261.                 Next X
  262.             Next Y
  263.  
  264.         Case 16
  265.             For Y = 0 To hgt - 1
  266.                 For X = 0 To wid - 1
  267.                     With pixels(X, Y)
  268.                         ' Get the combined 2 bytes for this pixel.
  269.                         two_bytes = bytes(X * 2, Y) + bytes(X * 2 + 1, Y) * 256&
  270.  
  271.                         ' Separate the pixel's components.
  272.                         .rgbBlue = two_bytes Mod 32
  273.                         two_bytes = two_bytes \ 32
  274.                         .rgbGreen = two_bytes Mod 64
  275.                         two_bytes = two_bytes \ 64
  276.                         .rgbRed = two_bytes
  277.                     End With
  278.                 Next X
  279.             Next Y
  280.  
  281.         Case 24
  282.             ' Blast the data from the pixels array
  283.             ' to the bytes array using CopyMemory.
  284.             For Y = 0 To hgt - 1
  285.                 CopyMemory pixels(0, Y), bytes(0, Y), wid * 3
  286.             Next Y
  287.  
  288.         Case 32
  289.             For Y = 0 To hgt - 1
  290.                 For X = 0 To wid - 1
  291.                     With pixels(X, Y)
  292.                         .rgbBlue = bytes(X * 4, Y)
  293.                         .rgbGreen = bytes(X * 4 + 1, Y)
  294.                         .rgbRed = bytes(X * 4 + 2, Y)
  295.                     End With
  296.                 Next X
  297.             Next Y
  298.  
  299.     End Select
  300. End Sub
  301. ' Set the bits in this PictureBox using a 0-based
  302. ' two-dimensional array of RGBTriplets. The pixels must
  303. ' have the right dimensions to match the picture.
  304. Public Sub SetBitmapPixels(ByVal pic As PictureBox, ByVal bits_per_pixel As Integer, pixels() As RGBTriplet)
  305. Dim wid_bytes As Long
  306. Dim wid As Integer
  307. Dim hgt As Integer
  308. Dim X As Integer
  309. Dim Y As Integer
  310. Dim bytes() As Byte
  311. Dim hpal As Long
  312. Dim two_bytes As Long
  313.  
  314.     ' See how big the image must be.
  315.     wid = UBound(pixels, 1) + 1
  316.     hgt = UBound(pixels, 2) + 1
  317.  
  318.     ' See how many bytes per row we need.
  319.     Select Case bits_per_pixel
  320.         Case 8
  321.             wid_bytes = wid
  322.         Case 15, 16
  323.             wid_bytes = wid * 2
  324.         Case 24
  325.             wid_bytes = wid * 3
  326.         Case 32
  327.             wid_bytes = wid * 4
  328.         Case Else
  329.             ' We don't understand this format.
  330.             Err.Raise bmphInvalidBitmapBits, _
  331.                 "DDBHelper.GetBitmapPixels", _
  332.                 "Invalid number of bits per pixel: " _
  333.                 & Format$(bits_per_pixel)
  334.     End Select
  335.  
  336.     ' Make sure it's even.
  337.     If wid_bytes Mod 2 = 1 Then wid_bytes = wid_bytes + 1
  338.  
  339.     ' Create the bitmap bytes array.
  340.     ReDim bytes(0 To wid_bytes - 1, 0 To hgt - 1)
  341.  
  342.     ' Set the bitmap byte values.
  343.     Select Case bits_per_pixel
  344.         Case 8
  345.             ' Use the nearest palette entries.
  346.             hpal = pic.Picture.hpal
  347.  
  348.             ' Get the RGB color components.
  349.             For Y = 0 To hgt - 1
  350.                 For X = 0 To wid - 1
  351.                     With pixels(X, Y)
  352.                         bytes(X, Y) = (&HFF And _
  353.                             GetNearestPaletteIndex(hpal, _
  354.                                 RGB(.rgbRed, .rgbGreen, .rgbBlue) _
  355.                             + &H2000000))
  356.                     End With
  357.                 Next X
  358.             Next Y
  359.  
  360.         Case 15
  361.             For Y = 0 To hgt - 1
  362.                 For X = 0 To wid - 1
  363.                     With pixels(X, Y)
  364.                         ' Keep the values in bounds.
  365.                         If .rgbRed > &H1F Then .rgbRed = &H1F
  366.                         If .rgbGreen > &H1F Then .rgbGreen = &H1F
  367.                         If .rgbBlue > &H1F Then .rgbBlue = &H1F
  368.  
  369.                         ' Combine the values in 2 bytes.
  370.                         two_bytes = .rgbBlue + 32 * (.rgbGreen + CLng(.rgbRed) * 32)
  371.  
  372.                         ' Set the byte values.
  373.                         bytes(X * 2, Y) = (two_bytes Mod 256) And &HFF
  374.                         bytes(X * 2 + 1, Y) = (two_bytes \ 256) And &HFF
  375.                     End With
  376.                 Next X
  377.             Next Y
  378.  
  379.         Case 16
  380.             For Y = 0 To hgt - 1
  381.                 For X = 0 To wid - 1
  382.                     With pixels(X, Y)
  383.                         ' Keep the values in bounds.
  384.                         If .rgbRed > &H1F Then .rgbRed = &H1F
  385.                         If .rgbGreen > &H3F Then .rgbGreen = &H3F
  386.                         If .rgbBlue > &H1F Then .rgbBlue = &H1F
  387.  
  388.                         ' Combine the values in 2 bytes.
  389.                         two_bytes = .rgbBlue + 32 * (.rgbGreen + CLng(.rgbRed) * 64)
  390.  
  391.                         ' Set the byte values.
  392.                         bytes(X * 2, Y) = (two_bytes Mod 256) And &HFF
  393.                         bytes(X * 2 + 1, Y) = (two_bytes \ 256) And &HFF
  394.  
  395.                     End With
  396.                 Next X
  397.             Next Y
  398.  
  399.         Case 24
  400.             ' Blast the data from the bytes array
  401.             ' to the pixels array using CopyMemory.
  402.             For Y = 0 To hgt - 1
  403.                 CopyMemory bytes(0, Y), pixels(0, Y), wid * 3
  404.             Next Y
  405.  
  406.         Case 32
  407.             For Y = 0 To hgt - 1
  408.                 For X = 0 To wid - 1
  409.                     With pixels(X, Y)
  410.                         bytes(X * 4, Y) = .rgbBlue
  411.                         bytes(X * 4 + 1, Y) = .rgbGreen
  412.                         bytes(X * 4 + 2, Y) = .rgbRed
  413.                     End With
  414.                 Next X
  415.             Next Y
  416.  
  417.     End Select
  418.  
  419.     ' Set the picture's bitmap bits.
  420.     SetBitmapBits pic.Image, wid_bytes * hgt, _
  421.         bytes(0, 0)
  422.     pic.Refresh
  423. End Sub
  424.